package state


import "api"


// 创建一个空的Lua表，将其推入栈顶
func (self *luaState) CreateTable(nArr, nRec int) {

	t := newLuaTable(nArr, nRec)

	self.stack.push(t)
}


// 在无法预估表的用法和容量时，创建表
func (self *luaState) NewTable() {

	self.CreateTable(0, 0)
}


// 根据键(从栈顶弹出)从表(索引由参数指定)里取值
// 然后把值推入栈顶并返回值的类型
func (self *luaState) GetTable(idx int) api.LuaType {

	 t := self.stack.get(idx)

	 k := self.stack.pop()

	 return self.getTable(t, k, false)	// 需要触发元方法
}


// 根据键从表里取值
// 指定索引处的值不是表，暂时调用panic()函数汇报错误
// 如果raw为true，表示需要忽略元方法
// 如果t是表，并且键已经在表中，或者需要忽略元方法，或者表里没有__index元方法，则维持上述逻辑
// 如果__index是表，则把表的访问操作转发给该表
// 否则，__index是函数，调用该函数
func (self *luaState) getTable(t, k luaValue, raw bool) api.LuaType {

	if tbl, ok := t.(*luaTable); ok {

		v := tbl.get(k)

		if raw || v != nil || !tbl.hasMetafield("__index") {

			self.stack.push(v)

			return typeOf(v)
		}
	}

	if !raw {

		if mf := getMetafield(t, "__index", self); mf != nil {

			switch x := mf.(type) {
			case *luaTable:

				return  self.getTable(x, k, false)

			case *closure:

				self.stack.push(mf)
				self.stack.push(t)
				self.stack.push(k)
				self.Call(2, 1)
				v := self.stack.get(-1)

				return typeOf(v)
			}

		}

	}

	panic("not a table!")
}


func (self *luaState) RawGet(idx int) api.LuaType {
	t := self.stack.get(idx)
	k := self.stack.pop()

	return self.getTable(t, k, true)  // 不需要调用元方法
}


// 和GetTable类似，只不过键不是从栈顶弹出的任意值，而是由参数传入的字符串
// 该方法专门从记录里获取字段
func (self *luaState) GetField(idx int, k string) api.LuaType {

	t := self.stack.get(idx)

	return self.getTable(t, k, false)  // 需要触发元方法
}


// 和GetField()方法类似，只不过由参数传入的是数字而非字符串
// 该方法时专门给数组准备的，用来根据索引获取数组元素
func (self *luaState) GetI(idx int, i int64) api.LuaType {

	t := self.stack.get(idx)

	return self.getTable(t, i, false)  // 需要触发元方法
}


func (self *luaState) RawGetI(idx int, i int64) api.LuaType {

	t := self.stack.get(idx)

	return self.getTable(t, i, true)  // 不需要触发元方法
}


// 看指定索引处的值是否有元表，如果有，则把元表推入栈顶并返回true
// 否则栈的状态不改变，返回false
func (self *luaState) GetMetatable(idx int) bool {

	val := self.stack.get(idx)

	if mt := getMetatable(val, self); mt != nil {

		self.stack.push(mt)

		return true
	} else {

		return false
	}
}


// 由于全局环境主要用来实现Lua全局变量的，所以里面的键基本上都是字符串
// 换句话说，全局环境主是被当成记录来使用的
// 为了便于操作，Lua API提供了GetGlobal()方法，可以把全局环境中的某个字段(名字由参数指定)推入栈顶
// 例如，栈里原有4个值.执行GetGlobal("k")之后，全局环境中名为"k"的字段被推入栈顶
func (self *luaState) GetGlobal(name string) api.LuaType {

	t := self.registry.get(api.LUA_RIDX_GLOBALS)

	return self.getTable(t, name, false)  // 可以調用元方法

}